package dnl.dev.memlog.sampler;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;


/**
 * @author daniel.or
 */
@SuppressWarnings("deprecation")
public class ProcessWrapper {

	private String name;
	private Process process;
	private PrintStream outputPrintStream;
	private PrintStream errPrintStream;
	private Thread errThread;
	private Thread outThread;
	private boolean printName;

	//~ Constructors
	// ---------------------------------------------------------------------------------

	// TODO: Handling of printing to out and err should be redesigned so not 
	// to include unnecessary 'if's
	
	/**
	 * Creates a new <code>ProcessWrapper</code>.
	 *  
	 */
	public ProcessWrapper(Process process, String processName, boolean threaded) {
		this.process = process;
		this.name = processName;
		this.setErrPrintStream(System.err, threaded);
		this.setOutputPrintStream(System.out, threaded);
	}

	public ProcessWrapper(Process process, String processName,
			PrintStream outputPrintStream, PrintStream errPrintStream, boolean threaded) {
		this(process, processName, outputPrintStream, errPrintStream, threaded, true);
	}
	
	/**
	 * Creates a new <code>ProcessWrapper</code>.
	 *  
	 */
	public ProcessWrapper(Process process, String processName,
			PrintStream outputPrintStream, PrintStream errPrintStream, boolean threaded, boolean printName) {
		this.process = process;
		this.name = processName;
		this.setErrPrintStream(errPrintStream, threaded);
		this.setOutputPrintStream(outputPrintStream, threaded);
		this.printName = printName;
	}

	//~ Methods
	// --------------------------------------------------------------------------------------

	public void joinStreamThreads() {
		try {
			errThread.join();
		}
		catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			outThread.join();
		}
		catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}

	/**
	 * @param errPrintStream
	 */
	public void setErrPrintStream(final PrintStream errPrintStream, boolean threaded) {
		if (process == null)
			return;
		this.errPrintStream = errPrintStream;

		final BufferedReader br = new BufferedReader(new InputStreamReader(process
				.getErrorStream()));
		Runnable r = squeezeReader(br, true);
		if (threaded) {
			if (errThread != null) {
				errThread.stop();
			}
			errThread = new Thread(r);
			errThread.start();
		}
		else {
			r.run();
		}
	}

	/**
	 * @return
	 */
	public PrintStream getErrPrintStream() {
		return errPrintStream;
	}

	/**
	 * @param outputPrintStream
	 */
	public void setOutputPrintStream(final PrintStream outputPrintStream, boolean threaded) {
		if (process == null)
			return;
		this.outputPrintStream = outputPrintStream;
		final BufferedReader br = new BufferedReader(new InputStreamReader(process
				.getInputStream()));

		Runnable r = squeezeReader(br, false);
		if (threaded) {
			if (outThread != null) {
				outThread.stop();
			}
			outThread = new Thread(r);
			outThread.start();
		}
		else {
			r.run();
		}
	}

	/**
	 * @return
	 */
	public PrintStream getOutputPrintStream() {
		return outputPrintStream;
	}

	/**
	 * @return
	 */
	public Process getProcess() {
		return process;
	}

	protected void processOutputLine(String line, String streamDesc, boolean err) {
		PrintStream ps = null;
		try {
			ps = err ? errPrintStream : outputPrintStream;
			if (printName) {
				ps.print("[Process ");
				ps.print(ProcessWrapper.this.name);
				ps.print("] ");
				ps.print(streamDesc);
				ps.print(": ");
			}
			ps.println(line);
		} finally {
			if(ps != null){
				ps.close();
			}
		}
	}
	
	private Runnable squeezeReader(final BufferedReader br, 
			final boolean err) {

		final String desc = err ? "err" : "out";
		Runnable r = new Runnable() {

			public void run() {
				try {
					String s = null;
					while ((s = br.readLine()) != null) {
						processOutputLine(s, desc, err);
					}
				}
				catch (IOException e) {
					processOutputLine("problem reading the process's streamDesc stream.\n"
							+ "reason: " + e.getMessage(), "ProcessWrapper err", true);
				}
			}
		};

		return r;
	}

}